#include <string.h>

#include "lib.h"

#include "jokguts.h"
#include "shue.h"
#include "vphand.h"
#include "jokcrdmg.h"

#include "pte_jok.h"	// PTE_xxx -> PayTableEvents...i.e. VPEVT_xxx

static JokerCardMgr jokMgr;

Shue*
JokerGuts::newShue(const ShueGameInfo &sgi)
{
	return new Shue(&jokMgr, 1, sgi);
}

CardMgr*
JokerGuts::getCardMgr()
{
	return &jokMgr;
}

int32
JokerGuts::CalcPayout(int32* pEvent, VPHand* pHand, int32 Bet,
	CalcPayoutMode, uint8* subGameData, int32 length)
{
	*pEvent = classifyHand(pHand);

	return *pEvent != VPEVT_LOSER ? PTBLENTRY(Bet-1, *pEvent) : 0;
}

int32
JokerGuts::classifyHand(VPHand* pHand)
{
	uint8 counts[13];
	int32 i;
	uint8 *probe, *first, *wild;
	bool8 flush = true;
	bool8 joker = false;
	uint8 card, prev;

	if (!pHand || (pHand && pHand->getCount() != 5))
		return VPEVT_LOSER;

	memset(counts, 0, 13);	// 2:index 0, 3:index 1 ... A:index 12

	prev = 0xff;
	for (i = 0 ; i < pHand->getCount(); i++)
	{
		card = pHand->getCardAt(i);

		if (!jokMgr.isJoker(card))
		{
			++counts[jokMgr.getRank(card) - 2];

			if (prev != 0xff && flush)
				flush = (bool8)(jokMgr.getSuit(card) == jokMgr.getSuit(prev));

			prev = card;
		}
		else
			joker = true;
	}

	if (joker)
	{
		// pair, 2 pairs, 3-of-a-kind, 4-of-a-kind, full house
		//
		if (memchr(counts, 4, 13))
			return VPEVT_5OFAKIND;

		if (memchr(counts, 3, 13))		// 4 of a kind pays > full house
			return VPEVT_4OFAKIND;

		probe = (uint8*)memchr(counts, 2, 13);
		if (probe)						// at least one pair
		{
			if (probe < counts+(13-1) &&	// 2 pair -> full house
				memchr(probe+1, 2, 13 - (probe-counts+1)))
				return VPEVT_FULLHOUSE;
			else						// one pair: 3 of a kind pays > 2 pair
				return VPEVT_3OFAKIND;
		}

		// straights: insert wild in sequence after first non-wild card
		// check for straights, zap inserted wild
		//
		wild = (uint8*)0;
		first = (uint8*)memchr(counts, 1, 13);
		probe = first + 1;
		while (!wild && probe < counts + 13)
		{
			if (*probe == 0)
			{
				wild = probe;
				*probe = 1;
			}
			++probe;
		}
		probe = first - 1;
		while (!wild && probe >= counts)
		{
			if (*probe == 0)
			{
				wild = probe;
				*probe = 1;
			}
			--probe;
		}

		first = probe = (uint8*)memchr(counts, 1, 13);
		i = 0;
		while (probe < counts + 13)
			if (*probe++ == 1)
				i++;
			else
				break;

		if (i == 5)
		{
			if (flush)
				if (first == counts + 10-2)
					return VPEVT_ROYALFLUSHJOKER;
				else
					return VPEVT_STRAIGHTFLUSH;

			return VPEVT_STRAIGHT;
		}

		*wild = 0;

		if (counts[CardMgr::ACE-2])		// ace-low straights
		{
			first = (uint8*)memchr(counts, 1, 13);
			probe = (uint8*)memchr(first+1, 1, 13 - (first+1 - counts));

			if (first <= counts+5-2 &&
				probe <= counts+5-2 &&
				memchr(probe+1, 1, 13 - (probe+1 - counts)) <= counts+5-2)
			{
				if (flush)
					return VPEVT_STRAIGHTFLUSH;

				return VPEVT_STRAIGHT;
			}
		}

		if (flush)
			return VPEVT_FLUSH;

		// xxx or better
		//
		probe = counts + (minPair - 2);
		while (probe < counts + 13)
			if (*probe++ == 1)
				return VPEVT_XXX_OR_BETTER;

		return VPEVT_LOSER;
	}
	else	// non-joker hand
	{
		// pair, 2 pairs, 3-of-a-kind, 4-of-a-kind, full house
		//
		if (memchr(counts, 4, 13))
			return VPEVT_4OFAKIND;

		if (memchr(counts, 3, 13))
			if (memchr(counts, 2, 13))
				return VPEVT_FULLHOUSE;
			else
				return VPEVT_3OFAKIND;

		probe = (uint8*)memchr(counts, 2, 13);
		if (probe)
		{
			if (probe < counts+(13-1) &&
				memchr(probe+1, 2, 13 - (probe-counts+1)))
				return VPEVT_2PAIR;
			else if (probe - counts >= minPair-2)
				return VPEVT_XXX_OR_BETTER;
		}

		// straights
		//
		first = probe = (uint8*)memchr(counts, 1, 13);
		i = 0;
		while (probe < counts + 13)
			if (*probe++ == 1)
				i++;
			else
				break;

		if (i == 5)
		{
			if (flush)
				if (first == counts + 10-2)
				{
					// check for sequential royal flush
					//
					uint8 rank = 10;	// expectation

					for (i = 0; i < pHand->getCount(); i++)
					{
						card = pHand->getCardAt(i);
						if (rank != jokMgr.getRank(card))
							break;

						++rank;
					}

					return i == pHand->getCount()
						? VPEVT_SEQROYALFLUSH
						: VPEVT_ROYALFLUSH;
				}
				else
					return VPEVT_STRAIGHTFLUSH;

			return VPEVT_STRAIGHT;
		}

		if (i == 4 &&								// ace-low straights
			counts[CardMgr::ACE-2] && first == counts)
		{
			if (flush)
				return VPEVT_STRAIGHTFLUSH;

			return VPEVT_STRAIGHT;
		}

		return flush ? VPEVT_FLUSH : VPEVT_LOSER;
	}
}

int32
JokerGuts::doubleUpCompare(uint8 dealer, uint8 player)
{
	//
	// NOTE WELL:  This function only works on a standard 52-card deck plus
	//			   a single joker (no other wild cards).
	//

	if (jokMgr.isJoker(dealer))
		return 1;						// dealer wins regardless
	else if (jokMgr.isJoker(player))
		return -1;						// player wins regardless
	else
		return VPGuts::doubleUpCompare(dealer, player);
}
